package com.study.example.gameserver.module.login;

import com.sparrow.cache.entity.EntityCache;
import com.sparrow.common.aoi.util.MapUtil;
import com.sparrow.common.attr.AttrBox;
import com.sparrow.common.attr.domain.Attr;
import com.sparrow.common.metric.MetricsManager;
import com.sparrow.common.ratelimit.LeakyBucket;
import com.sparrow.common.schedule.playerscheduler.PlayerScheduler;
import com.sparrow.common.thread.ThreadPool;
import com.sparrow.common.util.NameUtil;
import com.sparrow.common.util.PlayerHelper;
import com.sparrow.common.util.SignUtil;
import com.sparrow.common.util.WordUtil;
import com.sparrow.common.uuid.UUIDService;
import com.sparrow.event.api.EventBus;
import com.sparrow.event.annotation.EventReceiver;
import com.sparrow.event.model.Event;
import com.sparrow.resource.cache.ResourceCache;
import com.study.example.gameserver.base.ConfigService;
import com.sparrow.socket.tcp.anno.PacketHandler;
import com.sparrow.common.map.Position;
import com.study.example.gameserver.base.util.TipHelper;
import com.study.example.gameserver.config.SceneResource;
import com.study.example.gameserver.module.attr.domain.AttrType;
import com.study.example.gameserver.module.ban.service.BanService;
import com.study.example.gameserver.module.scene.manager.SceneManager;
import com.study.example.gameserver.module.scene.service.SceneService;
import com.study.example.gameserver.module.player.entity.Player;
import com.study.example.gameserver.module.player.event.PlayerInitEvent;
import com.study.example.gameserver.module.player.manager.PlayerManager;
import com.study.example.gameserver.module.player.service.PlayerService;
import com.study.example.gameserver.module.player.entity.PlayerEntity;
import com.sparrow.socket.tcp.session.impl.GameSession;
import com.sparrow.socket.tcp.session.manager.GameSessionManager;
import com.sparrow.socket.tcp.session.def.SessionConst;
import com.sparrow.example_message.base.req.ReqCreatePacket;
import com.sparrow.example_message.base.req.ReqLoginAuthPacket;
import com.sparrow.example_message.base.req.ReqLoginPacket;
import com.sparrow.example_message.base.resp.RespCreatePacket;
import com.sparrow.example_message.base.resp.RespLoginAuthPacket;
import com.sparrow.example_message.base.resp.RespLoginPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

@PacketHandler
@Component
public class LoginController {

    private static Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private PlayerService playerService;
    @Autowired
    private PlayerManager playerManager;
    @Autowired
    private EntityCache<Long, PlayerEntity> playerBaseInfoCache;
    @Autowired
    private UUIDService uuidService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private EventBus eventBus;
    @Autowired
    private GameSessionManager sessionManager;
    @Autowired
    private BanService banService;
    @Autowired
    private SceneService sceneService;
    @Autowired
    private SceneManager sceneManager;
    @Autowired
    private ResourceCache<Integer, SceneResource> sceneResCache;

    private LeakyBucket leakyBucket = new LeakyBucket(10, 100);
    /**
     * 登录验证
     * @param session
     * @param req
     */
    @EventReceiver
    public void reqLoginAuth(GameSession session, ReqLoginAuthPacket req) {
        logger.info("{} 请求登录验证执行", req.getAccount());
        if (req.getAccount() == null) {
            return;
        }
        if (!session.getStatus().compareAndSet(GameSession.INIT, GameSession.AUTHING)) {
            return;
        }

        //验证签名
        StringBuilder sb = new StringBuilder();
        sb.append(req.getAccount()).append("_");
        sb.append(req.getTime());
        String sign = SignUtil.generateSign(sb.toString(), configService.getAppKey());
        if (!sign.equals(req.getSign())) {
            logger.info("account:{} 签名验证不通过 remote sign:{} server sign:{}", req.getAccount(), req.getSign(), sign);
            session.close();
            return;
        }

        // 校验 IP 是否被封禁
        String ip = session.getIp(); // 假设 GameSession 提供了 IP 获取方法
        if (banService.isIpBanned(ip)) {
            logger.info("登录拦截：IP {} 已被封禁", ip);
            session.close();
            return;
        }
        // 校验账号是否被封禁
        if (banService.isAccountBanned(req.getAccount())) {
            logger.info("登录拦截：账号 {} 已被封禁", req.getAccount());
            session.close();
            return;
        }
        //限制登录速度
        if (!leakyBucket.tryAcquire()) {
            TipHelper.sendTip(session, "服务器爆满");
            return;
        }
        int server = configService.getServerId();

        String account = req.getAccount();
        session.setAttribute(SessionConst.ACCOUNT, account);
        Long playerId = playerService.getPlayerIdByAccountKey(PlayerHelper.toAccountKey(account, server));
        RespLoginAuthPacket resp = new RespLoginAuthPacket();
        if (playerId == null) {
            resp.setPlayerId(0);
        } else {
            GameSession oldSession = sessionManager.getSession(playerId);
            if (oldSession != null && oldSession != session) {
                logger.info("账号 {} 顶号：踢出旧会话 {}", account, oldSession.getId());
                oldSession.close();
            }
            PlayerEntity playerBaseInfo = playerBaseInfoCache.get(playerId);
            resp.setPlayerId(playerId);
            resp.setName(playerBaseInfo.getName());
            resp.setLevel(playerBaseInfo.getLevel());
            session.getStatus().set(GameSession.AUTHED);
        }
        session.write(resp);
    }

    /**
     * 请求创建角色
     * @param session
     * @param req
     */
    @EventReceiver
    public void reqCreateRole(GameSession session, ReqCreatePacket req) {
        String account = (String) session.getAttribute(SessionConst.ACCOUNT);
        if (account == null) {
            return;
        }
        int server = configService.getServerId();
        Long playerId = playerService.getPlayerIdByAccountKey(PlayerHelper.toAccountKey(account, server));
        if (playerId != null) {//已经创角了
            return;
        }
        //验证名字长度
        if (!NameUtil.validateLength(req.getName(), 2, 5)) {
            TipHelper.sendTip(session, "名字长度需为2~5");
            return;
        }
        //验证是否包含特殊字符
        if (!NameUtil.validName(req.getName())) {
            TipHelper.sendTip(session, "名字包含特殊字符");
            return;
        }
        //验证是否有敏感词
        if (WordUtil.isMatch(req.getName())) {
            TipHelper.sendTip(session, "名字包含敏感词");
            return;
        }
        //名字是否已经被使用
        if (playerService.containName(req.getName())) {
            TipHelper.sendTip(session, "名字已被使用");
            return;
        }

        playerId = uuidService.nextId(server);

        PlayerEntity playerBaseInfo = playerBaseInfoCache.getOrCreate(playerId, e -> {
            e.setAccount(account);
            e.setName(req.getName());
            e.setServer(server);
            e.setLevel(1);

            e.setSceneId(1);
            e.setLineId(1);
            e.setMapId(MapUtil.toMapId(e.getSceneId(), e.getLineId()));
            SceneResource sceneRes = sceneResCache.getResource(e.getSceneId());
            int[] spawn = sceneRes.getSpawn();
            Position pos = new Position(spawn[0], spawn[1], spawn[2]);
            e.setPos(pos);
        });

        playerManager.registerPlayerId(PlayerHelper.toAccountKey(playerBaseInfo.getAccount(), playerBaseInfo.getServer()), playerId);
        session.getStatus().set(GameSession.AUTHED);

        playerService.execPlayerAsync(playerId, p -> {
            long maxHp = p.getAttrBox().getFinalAttr().get(AttrType.Hp);
            p.setHp((int) maxHp);
            p.setMaxHp((int) maxHp);
            p.save();

            RespCreatePacket packet = new RespCreatePacket();
            packet.setName(playerBaseInfo.getName());
            packet.setLevel(playerBaseInfo.getLevel());
            packet.setPlayerId(p.getPlayerId());
            session.write(packet);
        });


    }

    /**
     * 请求登录角色
     * @param session
     * @param req
     */
    @EventReceiver
    public void reqLoginRole(GameSession session, ReqLoginPacket req) {
        if (session.getStatus().get() != GameSession.AUTHED) {
            return;
        }
        Object attribute = session.getAttribute(SessionConst.ACCOUNT);
        if (attribute == null) {
            return;
        }
        String account = (String) attribute;
        Long playerId = playerService.getPlayerIdByAccountKey(PlayerHelper.toAccountKey(account, configService.getServerId()));
        if (playerId == null) {
            return;
        }
        PlayerEntity playerBaseInfo = playerBaseInfoCache.get(playerId);
        if (playerBaseInfo == null) {
            return;
        }

        playerService.execPlayerAsync(playerId, p ->{
            bindSession(session, p);

            //初始化调度器
            if (p.getScheduler() == null) {
                p.setScheduler(new PlayerScheduler(ThreadPool.SCHEDULER, p));
            } else {
                p.getScheduler().reset();
            }

            AttrBox attrBox = p.getAttrBox();
            Attr finalAttr = attrBox.getFinalAttr();
            Map<Integer, Long> attrMap = finalAttr.toMap();

            RespLoginPacket resp = new RespLoginPacket();
            resp.setPlayerId(playerId);
            resp.setName(p.getPlayerName());
            resp.setLevel(p.getLevel());
            resp.setExp(p.getExp());
            resp.setFight(p.getEntity().getFightForce());
            resp.setSceneId(p.getSceneId());
            resp.setAttrMap(attrMap);
            resp.setHp(p.getHp());
            resp.setMaxHp(p.getMaxHp());
            session.write(resp);

            eventBus.publish(Event.newBuilder(new PlayerInitEvent(p)).sync().build());
            MetricsManager.counter("LoginCount").increment();
        });
    }

    private void bindSession(GameSession session, Player p) {
        session.setDelayFlush(false);
        session.setEntity(p);
        p.setSession(session);
        sessionManager.bind(session, p.getPlayerId());
    }
}
